package com.wei.czz.framework.game.helper;

import com.alibaba.fastjson2.JSON;
import com.wei.czz.common.constant.Constant;
import com.wei.czz.common.dto.game.ExportGameDto;
import com.wei.czz.common.dto.game.GameDto;
import com.wei.czz.common.enums.CommonEnum;
import com.wei.czz.common.enums.ResultEnum;
import com.wei.czz.common.enums.common.exportTask.TaskTypeEnum;
import com.wei.czz.common.enums.common.exportTask.ExportStatusEnum;
import com.wei.czz.common.exception.CzzException;
import com.wei.czz.common.po.UserPo;
import com.wei.czz.common.property.DownloadProperty;
import com.wei.czz.common.tool.PageDto;
import com.wei.czz.common.utils.ExcelUtils;
import com.wei.czz.common.utils.SecurityUtils;
import com.wei.czz.common.vo.common.exportTask.ExportTaskVo;
import com.wei.czz.common.vo.game.GameFormVo;
import com.wei.czz.framework.common.entity.ExportTaskEntity;
import com.wei.czz.framework.common.service.ExportTaskService;
import com.wei.czz.framework.game.entity.GameFlowEntity;
import com.wei.czz.framework.game.entity.GamePrizeEntity;
import com.wei.czz.framework.game.service.GameFlowService;
import com.wei.czz.framework.game.service.GamePrizeService;
import com.wei.czz.framework.game.service.GameService;
import com.wei.czz.framework.game.service.GameUserService;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.Validator;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wyw
 * date: 2024-04-12 14:36:40
 * className: GameManager
 * version: 1.0
 * description:
 */
@Component
@AllArgsConstructor
public class GameHelper {

    private static final Logger log = LoggerFactory.getLogger(GameHelper.class);

    private final DownloadProperty downloadProperty;

    private final GameService gameService;

    private final GameFlowService gameFlowService;

    private final GamePrizeService gamePrizeService;

    private final GameUserService gameUserService;

    private final ExportTaskService exportTaskService;

    private final Validator validator;

    public void validExportGameParams(ExportTaskVo exportTaskVo) {

        GameFormVo gameFormVo = JSON.parseObject(exportTaskVo.getParams(), GameFormVo.class);

        Errors errors = new BeanPropertyBindingResult(gameFormVo, GameFormVo.class.getSimpleName());
        // 校验
        validator.validate(gameFormVo, errors);
        if (errors.hasErrors()) {
            FieldError fieldError = errors.getFieldErrors().get(0);
            log.info("【游戏数据导出】参数值错误。field={} errorMessage={}", fieldError.getField(),
                    fieldError.getDefaultMessage());
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), fieldError.getDefaultMessage());
        }

        if (gameFormVo.getLimit() > downloadProperty.getMaxSize()) {
            log.error("导出游戏数据条数超过最大限制条数。download.size={} maxSize={}", gameFormVo.getLimit(),
                    downloadProperty.getMaxSize());
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "导出游戏数据条数超过最大限制条数");
        }

        /*Map<String, String> map = new LinkedHashMap<>();
        if (StringUtils.isNotBlank(gameFormVo.getWord())) {
            map.put("游戏名称", gameFormVo.getWord());
        }
        if (Objects.nonNull(gameFormVo.getStartTime())) {
            map.put("游戏开始时间", TimeUtils.yyyyMMddHHmmss.format(gameFormVo.getStartTime()));
        }
        if (Objects.nonNull(gameFormVo.getEndTime())) {
            map.put("游戏结束时间", TimeUtils.yyyyMMddHHmmss.format(gameFormVo.getEndTime()));
        }
        if (Objects.nonNull(gameFormVo.getStatus())) {
            if (CommonEnum.ZERO.getValue().equals(gameFormVo.getStatus())) {
                map.put("游戏状态", "开始");
            } else if (CommonEnum.ONE.getValue().equals(gameFormVo.getStatus())) {
                map.put("游戏状态", "停止");
            } else {
                map.put("游戏状态", "未知");
            }
        }
        map.put("页码", String.valueOf(gameFormVo.getPage()));
        map.put("每页大小", String.valueOf(gameFormVo.getLimit()));
        if ("id".equals(gameFormVo.getField())) {
            map.put("排序字段", "主键");
        } else if ("gameName".equals(gameFormVo.getField())) {
            map.put("排序字段", "游戏名称");
        } else if ("startTime".equals(gameFormVo.getField())) {
            map.put("排序字段", "开始时间");
        } else if ("endTime".equals(gameFormVo.getField())) {
            map.put("排序字段", "结束时间");
        } else if ("createTime".equals(gameFormVo.getField())) {
            map.put("排序字段", "创建时间");
        } else {
            map.put("排序字段", "未知字段");
        }

        if (Constant.ASCENDING.equals(gameFormVo.getOrder())) {
            map.put("排序方式", "升序");
        } else {
            map.put("排序方式", "降序");
        }*/

    }

    /**
     * 导出游戏数据操作
     * @param taskId   导出任务主键
     * @param response http响应对象
     */
    public void exportGame(Long taskId, HttpServletResponse response) {
        // 获取请求用户
        UserPo userPo = SecurityUtils.getUser();

        /*
            获取导出任务
         */
        ExportTaskEntity exportTask = exportTaskService.get(taskId);

        GameFormVo gameFormVo = JSON.parseObject(exportTask.getParams(), GameFormVo.class);

        log.info("开始执行游戏数据导出。{}", gameFormVo);
        Date start = new Date();

        /*
            获取游戏列表
         */
        PageDto<GameDto> pageDto = gameService.getGamePageList(gameFormVo);
        List<GameDto> gameList = pageDto.getList();
        // 收集游戏主键
        List<Long> idList = gameList.stream().map(GameDto::getId).map(Long::parseLong).collect(Collectors.toList());

        /*
            获取游戏奖品
         */
        List<GamePrizeEntity> gamePrizeList = gamePrizeService.getGamePrizeList(idList);

        /*
            获取参与游戏用户
         */
        List<GameFlowEntity> gameFlowList = gameFlowService.getGameFlowList(idList);

        log.info("开始封装要导出的游戏数据");

        Map<Long, Integer> gamePrizeNumMap = new HashMap<>();
        Map<Long, Integer> gamePrizeTotalNumMap = new HashMap<>();
        Map<Long, Integer> useGamePrizeNumMap = new HashMap<>();
        for (GamePrizeEntity gamePrize : gamePrizeList) {
            Long gameId = gamePrize.getGameId();

            int num = gamePrizeNumMap.getOrDefault(gameId, 0);
            gamePrizeNumMap.put(gameId, num + 1);

            num = gamePrizeTotalNumMap.getOrDefault(gameId, 0);
            gamePrizeTotalNumMap.put(gameId, num + gamePrize.getPrizeTotalNum());

            num = useGamePrizeNumMap.getOrDefault(gameId, 0);
            useGamePrizeNumMap.put(gameId, num + gamePrize.getPrizeUseNum());
        }

        Map<Long, Integer> gameFlowMap = gameFlowList.stream().collect(Collectors.groupingBy(
                GameFlowEntity::getGameId,
                Collectors.collectingAndThen(
                        Collectors.toList(),
                        list -> list.stream()
                                .map(GameFlowEntity::getUserId)
                                .distinct()
                                .map(e -> 1)
                                .reduce(0, Integer::sum)
                )
        ));
        // 序号
        AtomicInteger serial = new AtomicInteger(1);

        List<ExportGameDto> exportGameDtoList = gameList.stream()
                .map(gameDto -> {
                    long gameId = Long.parseLong(gameDto.getId());

                    ExportGameDto exportGameDto = new ExportGameDto();
                    exportGameDto.setSerial(serial.getAndIncrement());
                    exportGameDto.setGameName(gameDto.getGameName())
                            .setStartTime(gameDto.getStartTime())
                            .setEndTime(gameDto.getEndTime())
                            .setStatus(gameDto.getStatus())
                            .setStatusName(CommonEnum.ZERO.getValue().equals(gameDto.getStatus()) ? "运行中" : "停止")
                            .setGamePrizeNum(gamePrizeNumMap.get(gameId))
                            .setGamePrizeTotalNum(gamePrizeTotalNumMap.get(gameId))
                            .setUseGamePrizeNum(useGamePrizeNumMap.get(gameId))
                            .setGameUserNum(gameFlowMap.get(gameId));

                    return exportGameDto;
                })
                .collect(Collectors.toList());

        log.info("生成导出游戏数据完成");

        DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");

        String fields = exportTask.getFields();
        Integer status = ExportStatusEnum.SUCCESS.getValue();
        String errorMessage = StringUtils.EMPTY;

        try {
            // 文件名
            String fileName = TaskTypeEnum.GAME_EXPORT.getName() + Constant.SPLIT + dateFormat.format(new Date());

            // 数据写入输出流中
            ExcelUtils.writeResponse(fileName, TaskTypeEnum.GAME_EXPORT.getName(), exportGameDtoList,
                    ExportGameDto.class, response);

            fields = JSON.toJSONString(exportGameDtoList.get(0).getExportFieldList());

        } catch (Exception e) {
            log.info("游戏数据导出异常。message={}", e.getMessage(), e);

            status = ExportStatusEnum.FAIL.getValue();

            errorMessage = "游戏数据导出任务执行失败";

            response.reset();
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            try {
                response.getWriter().println(errorMessage);
            } catch (IOException ex) {
                log.error("异常结果返回输出失败。message={}", ex.getMessage());
            }
        }

        Date end = new Date();

        log.info("游戏数据导出任务执行结束。执行耗时：{}毫秒", end.getTime() - start.getTime());

        ExportTaskEntity updateExportTask = new ExportTaskEntity();
        updateExportTask.setTaskId(taskId)
                .setFields(fields)
                .setStatus(status)
                .setStartTime(start)
                .setEndTime(end)
                .setUpdateTime(end)
                .setUpdateUser(userPo.getUsername())
                .setUpdateUserId(userPo.getUserId())
                .setRemark(errorMessage);
        exportTaskService.saveExportTask(updateExportTask);
    }
}
